#include "FileDataStream.h"

#include <iostream>
#include <cstdlib>

using namespace Huffman;

FileDataStream::FileDataStream(std::string filePath, OpenMode openMode)
{	
	std::ios_base::openmode mode = openMode == Read ? std::fstream::in | std::fstream::binary : std::fstream::out | std::fstream::binary;
	bufferIndex = openMode == Read ? -1 : BufferSize - 1; /*TODO domyslna wartosc nie moze byc dobra jednoczesnie dla kodera i dekodera */
	this -> filePath = filePath;
	this -> stream = new std::fstream(filePath, mode);
	
	if(!stream -> is_open()) {
		std :: cerr << "Nie mozna otworzyc pliku: " << filePath << std::endl;
		exit(-1);
	}

	writeByteCounter = 0;
	readByteCounter = 0;
}

FileDataStream::~FileDataStream(void)
{
	// empty destructor
}

char FileDataStream::read() {	

	std::bitset<BufferSize> byteBuffer = 0;

	for(int i = BufferSize - 1; i >= 0; i--) {
		byteBuffer[i] = readBit();
	}	
	return (char) byteBuffer.to_ulong();
}


void FileDataStream::writeEncodedSymbol(EncodedSymbol* symbol) {

	// writes the single bits of the data
	// TODO rozmiar buffora nie jest dynamiczny - powinnien byc w tym wypadku symbol -> getLenght()
	std::bitset<32> byteBuffer = symbol -> getValue();

	for(int i = 0 ; i <= symbol -> getLenght() - 1; i++) {
		writeBit(byteBuffer[i]);
	}
}

void FileDataStream::write(char data) {

	/*std::cout << "DECODED: " << data  << std::endl;*/
	// writes the single bits of the data
	std::bitset<BufferSize> byteBuffer = data;

	for(int i = BufferSize - 1; i >= 0; i--) {
		writeBit(byteBuffer[i]);
	}
}

bool FileDataStream::readBit()
{
	if(bufferIndex < 0) 
	{
		readBuffer();
	}
	
	return buffer[bufferIndex--];
}

void FileDataStream::writeBit(bool bit)
{
	if(bufferIndex < 0) {
		writeBuffer();
	}

	buffer[bufferIndex--] = bit;
}

bool FileDataStream::isEof() {
	return stream -> eof();
}

void FileDataStream::flush() 
{	
	writeBuffer();
}

void FileDataStream::writeBuffer() 
{
	writeByteCounter++;

	/*std::cout << "ENCODED: " << (char)buffer.to_ulong() << std::endl;*/	
	stream -> put((char)buffer.to_ulong());

	buffer = 0;
	bufferIndex = BufferSize - 1;
}

void FileDataStream::readBuffer() 
{
	int c = stream -> get();

	if( c != -1) {
		readByteCounter++;
	}

	buffer = c;
	bufferIndex = BufferSize - 1;
}

void FileDataStream::close() 
{
	stream -> flush();
	stream -> close();
}

std::string FileDataStream::getPath()
{
	return filePath;
}